// ==UserScript==
// @name         守夜
// @version      3.0.2
// @description  首页js，alook和油猴通用
// @author       Uignaix
// @namespace    Uignaix [2536677]
// @run-at       document-end
// @license      MIT
// @match      https://www.torn.com/*
// ==/UserScript==

/**
 * 守夜增强插件3.0.2
 * 0. 在任意页面底部显示固定chain长度和倒计时
 * 1. 放大chain倒计时字体，颜色调整为红色
 * 2. 方便手机显示时间到底部
 * 3. chain快断的时候，屏幕闪烁红色背景，并语音提示
 * 4. 快到bonus时，屏幕闪烁黄色背景，并语音提示
 * 5. 可设置任意时间点语音提示
 *
 *  使用方式：
 *  固定面板通过调节配置代码可实现开关和最小激活长度，满足条件始终显示
 *
 *  可配置项：
 *  1. 是否开启固定面板 & 最小激活长度
 *  2. 语音开关， 可设置在多少秒的时候提醒 默认：180, 120, 60, 30, 15
 *  3.
 *  4. 危险线：chain快要断掉的剩余时间 默认 60s
 *  5. 哪些bonus提醒 & 距bonus还多远提醒 默认 10
 *
 *  3.0 更新
 *  1. 去除了帮派主页的调整，改为全局统一实现语音提示和闪烁
 *  2. 修复chain为0是不激活页面出现的负数
 *  3.0.1 更新
 *  时间为0以下，不显示
 *  3.0.2 修复ALOOK 浏览器里z-index太大导致的面板不能显示
 *  3.0.3
 *  由于开多个页面会导致通知播音，因此
 */

(function() {
    'use strict';

    // 如果加载到了 页面
    console.log('watcher loaded')
    window.chainOption = {
        // 是否开启面板
        backWatcher: true,
        // 最小激活面板的chain，太短的不看
        backWatcherMin: 0,
        // 是否开启语音提示
        voice: true,
        // 报时提示提示数组, chain最长300秒，设置那几秒报时
        voiceInfoArray: [120, 60, 30, 15],
        // 危险线（秒）, 描述在危险线一下，屏幕闪烁
        breakLine: 60,
        // 提醒的bonus快到了，快到bonus的时候，屏幕闪烁
        bonusList: [250, 500, 1000, 2500, 5000, 10000, 25000, 50000, 100000],
        // 离下个bonus还多少时提醒
        bonusVoiceGap: 10,

        // chain时间容器的class
        timer: '.chain-box-timeleft',

        // 是否快完了
        isNearBreak: false,
        // 是否快到bonus
        isNearBonus: false,
        // 语音过滤页面规则，只有符合规则的页面才会播放，避免多个页面一起放
        voiceFilter: "",
    }
    window.backWatcher = {
        $btn: null,
        key: 'is_watching',
        timeToUpdate: 0,
        // 固定倒计时的interval
        _timerInterval: null,
        // 用于固定展示的时间
        _timeToUpdate: 0,
        // chain断掉的时间点
        _timestampToUpdate: 0,
        // 当前chain
        _amount: 0,
        _sessionInterval: null,
        // 声音互斥锁
        _voiceLockKey: 'voice_lock_key'
    }

    if(location.href.startsWith('https://www.torn.com/factions.php?step=your')) {
        initChainWatch()
    }
    restoreBackWatcher()

// 插入检查按钮到状态栏
    function initChainWatch () {
        var $forum = $('.forum-thread.t-clear.c-pointer')
        backWatcher.$btn = $('<a role="button" class=" forum-thread t-clear h c-pointer  m-icon line-h24 right last"></a>')
        $forum.before(backWatcher.$btn)
        backWatcher.$btn.attr('href', 'javascript:void(0);')
        backWatcher.$btn.css('line-height', '20px')
        if(isWatching()) {
            backWatcher.$btn.text('停止守夜')
        }else {
            backWatcher.$btn.text('守夜开始')
        }
        backWatcher.$btn.on('click', function () {
            // 已开始则暂停，未开始则开始
            if (isWatching()) {
                stopWatch()
            } else {
                startWatch()
                voiceAnnouncements('大佬，语音提示已开启')
            }
        })

    }

    /**
     * 重置固定watcher
     */
    function restoreBackWatcher () {
        // 增加提示框
        setNearBonus()
        setNearBreak()
        addKeyFrame()
        fixTimer()
        updateSession()
        if(isWatching()){
            startWatch()
        }

    }


    /**
     * 开始
     */
    function startWatch () {
        // 按钮文字改变
        backWatcher.$btn && backWatcher.$btn.text('停止守夜')

        window.localStorage && localStorage.setItem(backWatcher.key, 'true');
        $('.fixed-timer').css('opacity', '1');
        updateSession()
        // 每秒更新时间
        backWatcher._timerInterval = setInterval(function () {
            // 剩余时间通过计算得出来
            backWatcher._timeToUpdate = backWatcher._timestampToUpdate - Math.floor(new Date().getTime()/1000)
            if(backWatcher._timeToUpdate<0) {
                backWatcher._timeToUpdate=0
            }
            $('.back-watcher-time').text(backWatcher._timeToUpdate);
            $('.back-watcher-count').text(backWatcher._amount);
            if(backWatcher._timeToUpdate<=0) {
                $('.fixed-timer').css('opacity', '0');
            }
            // 判读是否到点报时
            // 判断是否快断了
            // 判断bonus是否快到了
            check()

        }, 1000)
    }

    /**
     * 停止还原样式
     */
    function stopWatch () {
        // 按钮文字改变
        backWatcher.$btn.text('守夜开始')
        window.localStorage && localStorage.setItem(backWatcher.key, '');
        clearFixedTimer()
    }

    /**
     *  判断是否正在守夜
     */
    function isWatching() {
        return window.localStorage && localStorage.getItem(backWatcher.key)
    }

    /**
     * 周期循环检查
     */
    function check () {
        // 3分钟以下，每分钟定时播报
        voiceInfo()
        // 检查倒计时是否邻近
        checkCountdown()
        // 检查bonus是否邻近
        checkBonus()
    }

    /**
     * 检查倒计时是否邻近
     */
    function checkCountdown () {
        // 获取当前时间
        var left = backWatcher._timeToUpdate
        // 打破炫彩提醒
        if (left>1&&left <= chainOption.breakLine) {
            nearBreak(left)
        } else {
            clearNearBreak()
        }
    }

    /**
     * 检查Bonus是否邻近
     */
    function checkBonus () {
        // 当前chain进度
        var chainCount = backWatcher._amount
        // console.log("检查Bonus是否邻近", chainCount)
        // 检查是否邻近bonus
        chainOption.bonusList.forEach(bonus => {
            var gap = bonus - chainCount
            if (gap === chainOption.bonusVoiceGap) {
                // 恰好等于10，语音播报
                voiceAnnouncements('快到' + bonus + '的bonus了')
                chainOption.isNearBonus = true
            } else if (gap < chainOption.bonusVoiceGap && gap>0) {
                chainOption.isNearBonus = true
            } else {
                chainOption.isNearBonus = false
            }
        })
        // console.log("检查Bonus是否邻近",chainOption.isNearBonus)
        if (chainOption.isNearBonus) {
            bonusWarn()
        } else {
            clearBonusWarn()
        }
    }

    /**
     * 语音提醒
     */
    function voiceInfo () {
        var index = chainOption.voiceInfoArray.indexOf(backWatcher._timeToUpdate)
        // console.log('检车语音',index)
        if(index>=0) {
            voiceAnnouncements('大佬，只有'+chainOption.voiceInfoArray[index]+'秒了')
        }
    }

    /**
     * 即将结束提醒
     */
    function nearBreak (left) {
        // 到达指定时间是语音播报
        $('.near-break').css('animation', 'flash 1s infinite')
    }

    /**
     * 去除即将结束提醒
     */
    function clearNearBreak () {
        $('.near-break').css('animation','')
    }

    /**
     * 快到bonus警告
     */
    function bonusWarn () {
        $('.near-bonus').css('animation', 'flash 1s infinite')
    }

    function clearBonusWarn () {
        $('.near-bonus').css('animation','')
    }

    /**
     * 设置break报警样式
     */
    function setNearBreak () {
        var win = $('<div class="near-break" style="position: fixed;width: 100vw;top: 0;height: 100vh;left: 0;background: #ff7a7a38;pointer-events: none;"></div>')
        win.css('z-index', 999)
        win.css('opacity', 0)
        $('body').append(win)
    }

    /**
     * 设置bonus报警样式
     */
    function setNearBonus () {
        var win = $('<div class="near-bonus" style="position: fixed;width: 100vw;top: 0;height: 100vh;left: 0;background: rgb(255 251 122 / 8%);pointer-events: none;"></div>')
        win.css('z-index', 999)
        win.css('opacity', 0)
        $('body').append(win)
    }

    /**
     * 添加动画
     */
    function addKeyFrame () {
        // 闪烁动画
        const runkeyframes = '@keyframes flash{0%{opacity: 1}100%{opacity: 0}}'
        // 创建style标签
        const style = document.createElement('style')
        // 设置style属性
        style.type = 'text/css'
        // 将 keyframes样式写入style内
        style.innerHTML = runkeyframes
        // 将style样式存放到head标签
        document.getElementsByTagName('head')[0].appendChild(style)
    }

// 语音播报
    function voiceAnnouncements (str) {
        if (!chainOption.voice) {
            return
        }
        // 3.0.3 加一个互斥锁，同一时间，只有一个页面能够播音
        // 防止各个页面的时间差，每次互斥锁持续5s，因此会导致5s内无法多次播音
        // 百度语音合成：或者使用新版地址https://tsn.baidu.com/text2audio
        var lock = localStorage.getItem(backWatcher._voiceLockKey);
        if(!lock) {
            localStorage.setItem(backWatcher._voiceLockKey, 'lock')
            var url = 'http://tts.baidu.com/text2audio?lan=zh&ie=UTF-8&spd=5&text=' + encodeURI(str)
            var n = new Audio(url)
            n.src = url
            n.play()
            setTimeout(function(){
                localStorage.setItem(backWatcher._voiceLockKey, '')
            }, 5000)
        }
    };

    function testVoice () {
        voiceAnnouncements(`
  秋名山上路人稀，常有车手较高低；
  如今车道依旧在，不见当年老司机。
  司机车技今尚好，前端群里不寂寥；
  向天再借五百年，誓言各行领风骚。
  `)
        voiceAnnouncements(`
  秋名山上路人稀，常有车手较高低；
  如今车道依旧在，不见当年老司机。
  司机车技今尚好，前端群里不寂寥；
  向天再借五百年，誓言各行领风骚。
  `)
        voiceAnnouncements(`
  秋名山上路人稀，常有车手较高低；
  如今车道依旧在，不见当年老司机。
  司机车技今尚好，前端群里不寂寥；
  向天再借五百年，誓言各行领风骚。
  `)
        // 尝试了一些换女声的方式，但是，都失败了。。。
        voiceAnnouncements(`
  哇，代码写的真棒，你可真秀哇！
  `)
    }
// ================================================================
// 固定面板

    /**
     * 每秒去检查session是否有更新
     */
    function updateSession() {
        backWatcher._sessionInterval = setInterval(function() {
            var index = Object.keys(sessionStorage).findIndex(item => item.startsWith('sidebarData'));
            if(index>=0) {
                var sidebarData = JSON.parse(sessionStorage.getItem(sessionStorage.key(index)))
                var chainData = sidebarData.bars.chain
                var amount = chainData.amount
                // 如果amount不为0且amount有增长，说明chain有更新
                if(amount&&amount!==0&&amount>backWatcher._amount) {
                    // console.log('更新chain数据', backWatcher)
                    backWatcher._amount = chainData.amount
                    backWatcher._timestampToUpdate = chainData.timestampToUpdate

                    if(isWatching()&&backWatcher._amount>=chainOption.backWatcherMin) {
                        $('.fixed-timer').css('opacity', '1');
                    }else {
                        $('.fixed-timer').css('opacity', '0');
                    }
                }
            }
        }, 1000)
    }
    function clearUpdateSession() {
        backWatcher._amount = 0
        backWatcher._timeToUpdate = 0
        clearInterval(backWatcher._sessionInterval)
    }

    /**
     * 固定时间
     */
    function fixTimer () {
        var $fixed = $('.fixed-timer')
        if ($fixed.length === 0) {
            $fixed = $('<div class="fixed-timer" ' +
                'style="position: fixed;width: 100vw;bottom: 50px;height: 100px;pointer-events: none;opacity: 1;display: ' +
                'flex;z-index: 1000;justify-content: center;font-size: 100px;color: #de5e5ea8;align-items: center;"></div>')
            // 放两个数据
            var $count = $('<span class="back-watcher-count" style="font-size: 36px;text-shadow: 2px 2px 5px green;color: #066b58;"></span>')
            var $time = $('<span class="back-watcher-time" style="font-size: 100px;text-shadow: 2px 2px 5px #803b00;color: #e4510b;"></span>')
            $fixed.append($count).append($time)
            $('body').append($fixed)
        }


    }

    function clearFixedTimer () {
        clearUpdateSession();
        clearInterval(backWatcher._timerInterval)
        $('.fixed-timer').css('opacity', '0')
    }

})();
